﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AAA.Chart.Data;
using AAA.Chart.Indicator;
using AAA.Meta.Chart.Data;

namespace AAA.Chart.Component
{
    public partial class ChartContainer : UserControl, IDataSource
    {
        private Orientation _oOrientation = Orientation.Horizontal;
        private List<Control> _lstControls;
        private List<int> _lstHeight;
        private List<int> _lstWidth;
        private List<int> _lstHorizontalSplit;
        private int _iHorizontalSplitIndex = -1;
        private List<int> _lstVerticalSplit;
        private int _iVerticalSplitIndex = -1;
        private Cursor _cOriginalCursor;
        private bool _isMousePressed;
        private List<ChartX> _lstChart;
        private IDataSource _dataSource;
        private int _iStartIndex = -1;
        private int _iEndIndex = -1;
        private int _iDataCount = -1;
        private int _iXScale = 1;
        private int _iBarCount = -1;
        private HScrollBar _hScrollBar;

        public ChartContainer() : this(new DefaultDataSource())
        {
        }

        public ChartContainer(IDataSource dataSource)
        {
            InitializeComponent();

            _dataSource = dataSource;
            _lstControls = new List<Control>();
            _lstHeight = new List<int>();
            _lstWidth = new List<int>();
            _lstChart = new List<ChartX>();            

            ControlAdded += new ControlEventHandler(OnControlAdded);
            MouseEnter += new EventHandler(OnMouseEnter);
            MouseLeave += new EventHandler(OnMouseLeave);
            MouseDown += new MouseEventHandler(OnMouseDown);
            MouseUp += new MouseEventHandler(OnMouseUp);
            MouseMove += new MouseEventHandler(OnMouseMove);
            Resize += new EventHandler(OnResize);
            VisibleChanged += new EventHandler(OnVisibleChanged);

            _hScrollBar = new HScrollBar();
            _hScrollBar.Height = 15;
            _hScrollBar.ValueChanged += new EventHandler(OnValueChanged);
            Controls.Add(_hScrollBar);

        }
        
        void OnValueChanged(object sender, EventArgs e)
        {
            try
            {
                StartIndex = Math.Max(0, _hScrollBar.Value);
                EndIndex = CalculateEndIndex();
                RedrawChart();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        private void CalculateDataCount()        
        {
            try
            {
                foreach (string strSymbolId in _dataSource.SymbolList())
                    _iDataCount = Math.Max(_dataSource.GetLength(strSymbolId), _iDataCount);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        private int CalculateEndIndex()
        {
            return _iBarCount == 0
                    ?   _iDataCount
                    :   Math.Min(StartIndex + _iBarCount, _iDataCount);

        }

        /*
         * 計算畫面上可容納的K棒數 
         */
        private void CalculateBarCount()
        {
            _iBarCount = _lstChart.Count > 0
                            ? (int)Math.Floor((double)(_lstChart[0].DrawWidth) / (double)(XScale * ChartConstants.MIN_X_INTERVAL))
                            : 0;
        }

        public int XScale
        {
            get { return _iXScale; }
            set 
            { 
                _iXScale = value;

                CalculateBarCount();
                foreach (ChartX chart in _lstChart)
                    chart.XScale = value;
            }
        }
        
        public int StartIndex
        {
            get { return _iStartIndex; }
            set
            {
                _iStartIndex = value;
                EndIndex = CalculateEndIndex();
                foreach (ChartX chart in _lstChart)
                    chart.StartIndex = value;
            }
        }

        public int EndIndex
        {
            get { return _iEndIndex; }
            set
            {
                _iEndIndex = value;
                foreach (ChartX chart in _lstChart)
                    chart.EndIndex = value;
            }
        }

        public void RemoveAllChart()
        {

            for (int i = 0; i < _lstChart.Count; i++)
                Controls.Remove(_lstChart[i]);

            _lstControls = new List<Control>();
            _lstHeight = new List<int>();
            _lstWidth = new List<int>();
            _lstChart = new List<ChartX>();
        }

        void OnVisibleChanged(object sender, EventArgs e)
        {
            try
            {
                RecalculateComponentSize();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        void OnResize(object sender, EventArgs e)
        {
            try
            {
                RecalculateComponentSize();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            try
            {
                if ((_isMousePressed == false) || ((_iHorizontalSplitIndex < 0) && (_iVerticalSplitIndex < 0)) || (e.Y < 0) || (e.X < 0) || (e.Y > Height) || (e.X > Width))
                    return;

                int iUpLimit;
                int iLowLimit;

                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        iUpLimit = (_iHorizontalSplitIndex == 0) ? 0 : _lstHorizontalSplit[_iHorizontalSplitIndex - 1];
                        iLowLimit = (_iHorizontalSplitIndex == _lstHorizontalSplit.Count - 1)
                                        ? Height
                                        : _lstHorizontalSplit[_iHorizontalSplitIndex + 1];

                        if (e.Y <= iUpLimit) return;
                        if (e.Y >= iLowLimit) return;

                        _lstControls[_iHorizontalSplitIndex].Height += (e.Y - _lstHorizontalSplit[_iHorizontalSplitIndex]);
                        _lstControls[_iHorizontalSplitIndex + 1].Top = e.Y + 1;
                        _lstControls[_iHorizontalSplitIndex + 1].Height -= (e.Y - _lstHorizontalSplit[_iHorizontalSplitIndex]);
                        _lstHeight[_iHorizontalSplitIndex] = _lstControls[_iHorizontalSplitIndex].Height;
                        _lstHeight[_iHorizontalSplitIndex + 1] = _lstControls[_iHorizontalSplitIndex + 1].Height;
                        _lstHorizontalSplit[_iHorizontalSplitIndex] = e.Y;
                        break;

                    case Orientation.Vertical:
                        iUpLimit = (_iVerticalSplitIndex == 0) ? 0 : _lstVerticalSplit[_iVerticalSplitIndex - 1];
                        iLowLimit = (_iVerticalSplitIndex == _lstVerticalSplit.Count - 1)
                                        ? Width
                                        : _lstVerticalSplit[_iVerticalSplitIndex + 1];

                        if (e.X <= iUpLimit) return;
                        if (e.X >= iLowLimit) return;

                        _lstControls[_iVerticalSplitIndex].Width += (e.X - _lstVerticalSplit[_iVerticalSplitIndex]);
                        _lstControls[_iVerticalSplitIndex + 1].Left = e.X + 1;
                        _lstControls[_iVerticalSplitIndex + 1].Width -= (e.X - _lstVerticalSplit[_iVerticalSplitIndex]);
                        _lstWidth[_iVerticalSplitIndex] = _lstControls[_iVerticalSplitIndex].Width;
                        _lstWidth[_iVerticalSplitIndex + 1] = _lstControls[_iVerticalSplitIndex + 1].Width;
                        _lstVerticalSplit[_iVerticalSplitIndex] = e.X;
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        void OnMouseDown(object sender, MouseEventArgs e)
        {
            try
            {
                _isMousePressed = true;
                _iHorizontalSplitIndex = _lstHorizontalSplit == null ? -1 : _lstHorizontalSplit.IndexOf(e.Y);
                _iVerticalSplitIndex = _lstVerticalSplit == null ? -1 : _lstVerticalSplit.IndexOf(e.X);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        void OnMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                _isMousePressed = false;
                RecalculateComponentSize();

                RedrawChart();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        void OnMouseLeave(object sender, EventArgs e)
        {
            Cursor = _cOriginalCursor;
        }

        public Orientation Orientation
        {
            get { return _oOrientation; }
            set { _oOrientation = value; }
        }

        void OnMouseEnter(object sender, EventArgs e)
        {
            _cOriginalCursor = Cursor;
            switch (_oOrientation)
            {
                case Orientation.Horizontal:
                    Cursor = Cursors.HSplit;
                    break;

                case Orientation.Vertical:
                    Cursor = Cursors.VSplit;
                    break;
            }
        }

        private void ResetScrollRange()
        {
            _hScrollBar.Maximum = Math.Max(0, _iDataCount - _iBarCount + 3);
            //hScrollBar.Maximum = _iDataCount;
            //hScrollBar.Minimum = Math.Max(0, hScrollBar.Maximum - _iBarCount);
            _hScrollBar.Minimum = 0;
        }

        public void RedrawChart()
        {
            try
            {
                for (int i = 0; (i < _lstChart.Count - 1) && (Orientation == Orientation.Horizontal); i++)
                    _lstChart[i].ShowXAxis = false;

                for (int i = 1; (i < _lstChart.Count) && (Orientation == Orientation.Vertical); i++)
                    _lstChart[i].ShowYAxis = false;

                CalculateBarCount();
                EndIndex = CalculateEndIndex();
                ResetScrollRange();

                foreach (ChartX chart in _lstChart)
                    chart.PaintChart();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        public void RecalculateComponentSize()
        {
            try
            {
                _hScrollBar.Height = 15;
                _hScrollBar.Visible = (Orientation == Orientation.Horizontal) && 
                                     ((ClientSize.Width - ChartConstants.DEFAULT_AXIS_WIDTH) < _iDataCount * (XScale * ChartConstants.MIN_X_INTERVAL));
                _hScrollBar.Top = ClientSize.Height - _hScrollBar.Height;
                _hScrollBar.Left = 0;
                _hScrollBar.Width = ClientSize.Width;

                int iPosition = 0;
                int iTotal = 0;
                int iVisibleHeight = _hScrollBar.Visible ? ClientSize.Height - _hScrollBar.Height : ClientSize.Height;
                int iVisibleWidth = Width;                              

                switch(Orientation)
                {
                    case Orientation.Horizontal:
                        for (int i = 0; i < _lstHeight.Count; i++)
                            iTotal += _lstHeight[i];

                        _lstHorizontalSplit = new List<int>();
                        for (int i = 0; i < _lstControls.Count; i++)
                        {
                            _lstHeight[i] = (int)((double)iVisibleHeight * ((double)_lstHeight[i] / (double)iTotal));
                            
                            _lstControls[i].Top = iPosition;
                            _lstControls[i].Left = 0;
                            _lstControls[i].Width = ClientSize.Width;
                            _lstControls[i].Height = _lstHeight[i];
                            //_lstControls[i].Height = _lstHeight[i] + iPosition > iTotal ? iTotal - iPosition : _lstHeight[i];
                            iPosition += _lstHeight[i] + 1;
                            _lstHorizontalSplit.Add(iPosition - 1);
                        }
                        break;

                    case Orientation.Vertical:
                        for (int i = 0; i < _lstWidth.Count; i++)
                            iTotal += _lstWidth[i];

                        _lstVerticalSplit = new List<int>();
                        for (int i = 0; i < _lstControls.Count; i++)
                        {
                            _lstWidth[i] = (int)((double)iVisibleWidth * ((double)_lstWidth[i] / (double)iTotal));

                            _lstControls[i].Top = 0;
                            _lstControls[i].Left = iPosition;
                            _lstControls[i].Width = _lstWidth[i];
                            //_lstControls[i].Width = (_lstWidth[i] + iPosition > iTotal ? iTotal - iPosition : _lstWidth[i]);
                            _lstControls[i].Height = ClientSize.Height;
                            iPosition += _lstWidth[i] + 1;
                            _lstVerticalSplit.Add(iPosition - 1);
                        }

                        break;
                }

                CalculateBarCount();
                if (_hScrollBar.Visible)
                {
                    ResetScrollRange();
                    StartIndex = _hScrollBar.Value;
                    EndIndex = CalculateEndIndex();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "," + ex.StackTrace);
            }
        }

        public void OnControlAdded(object sender, ControlEventArgs e)
        {
            try
            {
                int iMinHeight = Int32.MaxValue;
                int iMinWidth = Int32.MaxValue;
                int iTotal = 0;
                
                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        for (int i = 0; i < _lstHeight.Count; i++)
                        {
                            if (iMinHeight > _lstHeight[i])
                                iMinHeight = _lstHeight[i];
                            iTotal += _lstHeight[i];
                        }
                        iMinHeight = (iMinHeight == Int32.MaxValue) ? Height : iMinHeight;
                        iTotal += iMinHeight;
                        
                        _lstControls.Add(e.Control);
                        _lstHeight.Add(iMinHeight);
                        break;

                    case Orientation.Vertical:
                        for (int i = 0; i < _lstWidth.Count; i++)
                        {
                            if (iMinWidth > _lstWidth[i])
                                iMinWidth = _lstWidth[i];
                            iTotal += _lstWidth[i];
                        }
                        iMinWidth = (iMinWidth == Int32.MaxValue) ? Width : iMinWidth;
                        iTotal += iMinWidth;

                        _lstControls.Add(e.Control);
                        _lstWidth.Add(iMinWidth);
                        break;
                }

            }
            catch (Exception ex)
            {
                
            }
            CalculateBarCount();
        }

        public ChartX GetChart(int iIndex)
        {
            return (iIndex >= 0) && (iIndex < _lstChart.Count)
                    ? _lstChart[iIndex]
                    : null;
        }

        public List<ChartX> GetAllCharts()
        {
            return _lstChart;
        }

        public void AddChart(ChartX cChart)
        {
            if (_lstChart.Contains(cChart))
                return;

            cChart.SetDataSource(this);
            cChart.XScale = XScale;
            cChart.StartIndex = StartIndex;
            cChart.EndIndex = EndIndex;
            Controls.Add(cChart);
            _lstChart.Add(cChart);            
            RecalculateComponentSize();            
        }

        public void AddIndicator(int iChartIndex, IIndicator indicator)
        {
            if((iChartIndex >= 0) && (iChartIndex < _lstChart.Count))
                _lstChart[iChartIndex].AddIndicator(indicator);   
        }

        #region IDataSource Members        
        public float LastPrice(string strSymbolId)
        {
            return _dataSource.LastPrice(strSymbolId);
        }

        public float LastVolume(string strSymbolId)
        {
            return _dataSource.LastVolume(strSymbolId);
        }

        public void AddTick(string strSymbolId, float fPrice, float fVolume)
        {
            _dataSource.AddTick(strSymbolId, fPrice, fVolume);
        }

        public void RemoveAllSymbol()
        {
            _dataSource.RemoveAllSymbol();
        }

        public void AddSymbol(string strSymbol, BarData barData)
        {
            /*
                        if (_dicBarData.ContainsKey(strSymbol))
                            return;
                        _lstSymbol.Add(strSymbol);
                        _dicBarData.Add(strSymbol, barData);
             */
            _dataSource.AddSymbol(strSymbol, barData);
            _iDataCount = Math.Max(barData.GetLength(), _iDataCount);
            
            CalculateBarCount();
            StartIndex = 0;
            //EndIndex = Math.Max(barData.GetLength(), EndIndex);
            EndIndex = CalculateEndIndex();            
        }

        public void AddSymbol(string strSymbol, Dictionary<string, PriceVolumeData> dicPriceVolumeData)
        {
            _dataSource.AddSymbol(strSymbol, dicPriceVolumeData);            
        }

        public List<string> SymbolList()
        {
            return _dataSource.SymbolList();
        }

        public bool ContainsSymbol(string strSymbolId)
        {
            //return _dicBarData.ContainsKey(strSymbolId);
            return _dataSource.ContainsSymbol(strSymbolId);
        }

        public PriceVolumeData GetPriceVolume(string strSymbolId)
        {
            return _dataSource.GetPriceVolume(strSymbolId);
        }

        public PriceVolumeData GetPriceVolume(string strSymbolId, string strStartDate, string strEndDate)
        {
            return _dataSource.GetPriceVolume(strSymbolId, strStartDate, strEndDate);
        }

        public BarData GetData(string strSymbolId)
        {
            //return _dicBarData.ContainsKey(strSymbolId) ? _dicBarData[strSymbolId] : new BarData();
            return _dataSource.GetData(strSymbolId);
        }
        
        public int GetLength(string strSymbolId)
        {
            //return _dicBarData.ContainsKey(strSymbolId) ? _dicBarData[strSymbolId].GetLength() : 0;
            return _dataSource.GetLength(strSymbolId);
        }
        #endregion
    }
}
